package com.xinzhidi.web.netty.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinzhidi.common.constant.AgreementConstants;
import com.xinzhidi.common.core.domain.model.LoginUser;
import com.xinzhidi.common.core.page.TableDataInfo;
import com.xinzhidi.common.utils.DateUtils;
import com.xinzhidi.common.utils.SecurityUtils;
import com.xinzhidi.common.utils.StringUtils;
import com.xinzhidi.common.utils.bean.BeanUtils;
import com.xinzhidi.web.build.mapper.BuildUnitMapper;
import com.xinzhidi.web.build.mapper.CBuildMapper;
import com.xinzhidi.web.dtuusr.domain.DtuUsr;
import com.xinzhidi.web.dtuusr.service.DtuUsrService;
import com.xinzhidi.web.equipment.domain.Equipment;
import com.xinzhidi.web.house.mapper.HouseholdMapper;
import com.xinzhidi.web.netty.entity.AgreementCommand;
import com.xinzhidi.web.netty.entity.DeviceInstruction;
import com.xinzhidi.web.netty.entity.dto.DeviceInstructionAddDto;
import com.xinzhidi.web.netty.entity.dto.DeviceInstructionListDto;
import com.xinzhidi.web.netty.entity.dto.DeviceInstructionUpdDto;
import com.xinzhidi.web.netty.entity.po.AgreementCommandPo;
import com.xinzhidi.web.netty.entity.vo.DeviceInstructionInfoVo;
import com.xinzhidi.web.netty.entity.vo.DeviceInstructionListVo;
import com.xinzhidi.web.netty.entity.vo.DeviceInstructionStatusVo;
import com.xinzhidi.web.netty.mapper.DeviceInstructionMapper;
import com.xinzhidi.web.netty.server.SocketHandler;
import com.xinzhidi.web.netty.utils.FormatData;
import com.xinzhidi.web.netty.utils.HexadecimalUtil;
import com.xinzhidi.web.onlineMonitor.domain.po.MeterReadPo;
import com.xinzhidi.web.onlineMonitor.service.OnlineMonitorService;
import com.xinzhidi.web.plot.mapper.CPlotMapper;
import io.netty.channel.ChannelHandlerContext;
import org.apache.commons.compress.utils.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;


/**
 * 指令发送Service业务层处理
 *
 * @author cx
 * @date 2023-06-16
 */
@Service
public class DeviceInstructionService extends ServiceImpl<DeviceInstructionMapper, DeviceInstruction> {

    private static final Logger log = LoggerFactory.getLogger(DeviceInstructionService.class);

    @Autowired
    private DeviceInstructionMapper deviceInstructionMapper;

    @Autowired
    private DtuUsrService dtuUsrService;

    @Autowired
    private CPlotMapper cplotMapper;

    @Autowired
    private CBuildMapper cbuildMapper;

    @Autowired
    private BuildUnitMapper buildUnitMapper;

    @Autowired
    private HouseholdMapper householdMapper;

    @Autowired
    private AgreementCommandService agreementCommandService;

    @Autowired
    private OnlineMonitorService monitorService;

    /**
     * 查询需要发送的指令
     *
     * @param dtuId   dtuId
     * @param address 设备地址
     * @return
     */
    public DeviceInstruction getLatestDeviceInstruct(Long dtuId, String address) {
        LambdaQueryWrapper<DeviceInstruction> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(DeviceInstruction::getStatus, AgreementConstants.InstructSendingState.BEEN_SENT)
                .eq(DeviceInstruction::getDtuId, dtuId)
                .eq(StringUtils.isNotBlank(address), DeviceInstruction::getAddress, address)
                .orderByAsc(DeviceInstruction::getCreateTime).last(" limit 1");
        DeviceInstruction deviceInstruction = deviceInstructionMapper.selectOne(wrapper);
        return deviceInstruction;
    }

    /**
     * 查询指令发送
     *
     * @param id 指令发送主键
     * @return 指令发送
     */
    public DeviceInstructionInfoVo selectDeviceInstructionById(Long id) {
        DeviceInstruction deviceInstruction = deviceInstructionMapper.selectById(id);
        DeviceInstructionInfoVo infoVo = new DeviceInstructionInfoVo();
        BeanUtils.copyProperties(deviceInstruction, infoVo);
        return infoVo;
    }

    /**
     * 查询指令发送列表
     *
     * @param dto 查询指令发送dto
     * @return 指令发送
     */
    public TableDataInfo<DeviceInstructionListVo> selectDeviceInstructionList(DeviceInstructionListDto dto) {

        List<DeviceInstructionListVo> list = this.baseMapper.getInfo(dto);

        return TableDataInfo.getDataTable(list, (domain -> {
            DeviceInstructionListVo vo = new DeviceInstructionListVo();
            BeanUtils.copyProperties(domain, vo);
            return vo;
        }));
    }

    /**
     * 新增指令发送
     *
     * @param dto 新增指令发送dto
     * @return 结果
     */
    public int insertDeviceInstruction(DeviceInstructionAddDto dto) {
        LoginUser loginUser = SecurityUtils.getLoginUser();

        DeviceInstruction deviceInstruction = new DeviceInstruction();
        BeanUtils.copyProperties(dto, deviceInstruction);
        deviceInstruction.setCreateBy(loginUser.getUserId().toString());
        deviceInstruction.setCreateTime(DateUtils.getNowDate());
        return deviceInstructionMapper.insert(deviceInstruction);
    }

    /**
     * 修改指令发送
     *
     * @param dto 修改指令发送dto
     * @return 结果
     */
    public int updateDeviceInstruction(DeviceInstructionUpdDto dto) {
        LoginUser loginUser = SecurityUtils.getLoginUser();

        DeviceInstruction deviceInstruction = new DeviceInstruction();
        BeanUtils.copyProperties(dto, deviceInstruction);
        return deviceInstructionMapper.updateById(deviceInstruction);
    }

    /**
     * 批量删除指令发送
     *
     * @param ids 需要删除的指令发送主键
     * @return 结果
     */
    public int deleteDeviceInstructionByIds(Long[] ids) {
        return deviceInstructionMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除指令发送信息
     *
     * @param id 指令发送主键
     * @return 结果
     */
    public int deleteDeviceInstructionById(Long id) {
        return deviceInstructionMapper.deleteById(id);
    }

    //    @Scheduled(cron = "0/2 * * * * ?")
    public void sendMessage() {
        //查询最新一条状态为0的待发送指令
        LambdaQueryWrapper<DeviceInstruction> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(DeviceInstruction::getStatus, AgreementConstants.InstructSendingState.WAIT_TO_SENT)
                .orderByAsc(DeviceInstruction::getCreateTime).last(" limit 1");
        DeviceInstruction deviceInstruction = this.getOne(wrapper);
        if (deviceInstruction != null) {
            DtuUsr dtuUsr = dtuUsrService.getById(deviceInstruction.getDtuId());
            byte[] bytes = FormatData.hex2bytes(deviceInstruction.getInstructionSet());
            ChannelHandlerContext channelHandlerContext = SocketHandler.CHANNEL_CTX_MAP.get(dtuUsr.getUuid());
            if (channelHandlerContext != null) {
                channelHandlerContext.writeAndFlush(bytes);
                deviceInstruction.setStatus(AgreementConstants.InstructSendingState.BEEN_SENT);
            } else {
                deviceInstruction.setStatus(AgreementConstants.InstructSendingState.SEND_FAILURE);
                log.info("未获取到下发通道！");
            }
            this.updateById(deviceInstruction);

        }
    }

    /**
     * 获取指令集合
     *
     * @param businessType  业务类型（1：手动抄表  2：自动抄表 3：初始设置  4：平衡调控 5：存档恢复）
     * @param protocolType  协议类型 1：户用阀门协议 2：单元阀门协议  3：室温采集器协议 4：户用热量表协议 5：单元热量表协议
     * @param type          类型（1：发送  2:接收）
     * @param instructType  指令类型（1：读取  2：设置）
     * @param equipmentList 设备集合
     * @param value         下发的值(调用前已转为十六进制：注意系数)
     * @return
     * @apiNote 获取某一批设备需要下发的指令集合。指令值是一样的，比如value代表开度，那么就是对equipmentList下发一样的设置开度命令
     */
    public List<DeviceInstruction> getDeviceInstructionList(Integer businessType, String protocolType, String type, String instructType, List<Equipment> equipmentList, String value) {
        List<DeviceInstruction> deviceInstructionList = Lists.newArrayList();
        if (CollectionUtils.isEmpty(equipmentList)) {
            return deviceInstructionList;
        }
        AgreementCommandPo agreementCommandPo = new AgreementCommandPo();
        agreementCommandPo.setProtocolType(protocolType);
        agreementCommandPo.setType(type);
        agreementCommandPo.setInstructType(instructType);
        AgreementCommand agreementCommand = agreementCommandService.getProtocolInstruction(agreementCommandPo);

        DeviceInstruction deviceInstruction;
        for (Equipment equipment : equipmentList) {
            deviceInstruction = new DeviceInstruction();
            deviceInstruction.setDtuId(equipment.getDtuId());
            deviceInstruction.setBusinessType(businessType);
            deviceInstruction.setProtocolType(protocolType);
            deviceInstruction.setProtocolName(agreementCommand.getProtocolName());
            deviceInstruction.setInstructType(instructType);
            deviceInstruction.setInstructName(agreementCommand.getInstructName());
            deviceInstruction.setPlotId(equipment.getLotId());
            deviceInstruction.setBuildId(equipment.getBuildId());
            deviceInstruction.setUnitId(equipment.getUnitId());
            deviceInstruction.setHouseholdId(equipment.getHouseholdId());
            deviceInstruction.setAddress(equipment.getAddress());
            //组装指令
            String instructionSet = "";
            instructionSet += agreementCommand.getPrefix();
            instructionSet += agreementCommand.getStartChar();
            instructionSet += agreementCommand.getDeviceType();
            instructionSet += equipment.getAddress();
            instructionSet += agreementCommand.getControlCode();
            instructionSet += agreementCommand.getLengthField();
            instructionSet += agreementCommand.getDataFieldInstruct();
            instructionSet += agreementCommand.getDataMark();
            instructionSet += value;
            //计算校验码
            String checkCode = HexadecimalUtil.getCheckCode(instructionSet);
            instructionSet += checkCode + agreementCommand.getEndMark();
            deviceInstruction.setInstructionSet(instructionSet);
            deviceInstructionList.add(deviceInstruction);
        }
        return deviceInstructionList;
    }

    /**
     * 获取未收到回复的指令
     *
     * @return java.util.List<com.xinzhidi.web.netty.entity.DeviceInstruction>
     * @author cx
     * @date 2023/7/29 16:43
     **/
    public List<DeviceInstruction> getNoReplyMessage() {
        List<DeviceInstruction> deviceInstructionList = deviceInstructionMapper.getNoReplyMessage();
        return deviceInstructionList;
    }

    /**
     * 获取最新一条待发送指令
     *
     * @return com.xinzhidi.web.netty.entity.DeviceInstruction
     * @date 2023/7/29 16:43
     **/
    public List<DeviceInstruction> getDeviceMessage(Integer port) {
        List<DeviceInstruction> deviceInstruction = deviceInstructionMapper.getDeviceMessage(port);
        return deviceInstruction;
    }

    /**
     * 获取重连指令
     *
     * @return com.xinzhidi.web.netty.entity.DeviceInstruction
     * @date 2023/7/29 16:43
     **/
    public List<DeviceInstruction> getReconnection(Integer port) {
        List<DeviceInstruction> deviceInstruction = deviceInstructionMapper.getReconnection(port);
        return deviceInstruction;
    }


    public void assembleMessage(List<Equipment> list , Integer exceedTimes , Integer businessType){
        String protocolType;
        MeterReadPo meterReadPo;
        DeviceInstruction deviceInstruction = null;
        List<DeviceInstruction> deviceInstructionList = Lists.newArrayList();
        for (Equipment equipment : list) {
            if(equipment.getAddress().equals("83610643000000")){
                System.out.println(111);
            }
            meterReadPo = new MeterReadPo();
            protocolType = equipment.getProtocolType();
            meterReadPo.setPlotId(equipment.getLotId());
            meterReadPo.setBuildId(equipment.getBuildId());
            meterReadPo.setUnitId(equipment.getUnitId());
            switch (protocolType) {
                case AgreementConstants.AgreementType.HOUSEHOLD_VALVE:
                    if(equipment.getAddress().equals("83610643000000")){
                        System.out.println(111);
                    }
                    //户用阀门协议
                    meterReadPo.setValveDtuId(equipment.getDtuId());
                    meterReadPo.setValveAddress(equipment.getAddress());
                    meterReadPo.setHouseholdId(equipment.getHouseholdId());
                    List<DeviceInstruction> li = monitorService.readHouseholdValveMeter(meterReadPo);
                    if(CollectionUtils.isNotEmpty(li)){
                        deviceInstructionList.addAll(li);
                    }
                    break;
                case AgreementConstants.AgreementType.UNIT_VALVE:
                    if(equipment.getAddress().equals("83610643000000")){
                        System.out.println(111);
                    }
                    //单元阀门协议
                    meterReadPo.setValveAddress(equipment.getAddress());
                    meterReadPo.setValveDtuId(equipment.getDtuId());
                    List<DeviceInstruction> unitList = monitorService.readBuildUnitValveMeter(meterReadPo);
                    if (CollectionUtils.isNotEmpty(unitList)) {
                        unitList.stream().forEach(item -> {
                            item.setAddress(equipment.getAddress());
                            item.setBusinessType(AgreementConstants.InstructBusinessType.AUTO_METER_READ);
                        });
                        deviceInstructionList.addAll(unitList);
                    }
                    break;
                case AgreementConstants.AgreementType.HOUSEHOLD_HEAT_METER:
                    if(equipment.getAddress().equals("83610643000000")){
                        System.out.println(111);
                    }
                    //户用热量表协议
                    meterReadPo.setHeatAddress(equipment.getAddress());
                    meterReadPo.setHeatDtuId(equipment.getDtuId());
                    meterReadPo.setHouseholdId(equipment.getHouseholdId());
                    List<DeviceInstruction> l = monitorService.readHouseholdHeatMeter(meterReadPo);
                    if(CollectionUtils.isNotEmpty(l)){
                        deviceInstructionList.addAll(l);
                    }
                    break;
                case AgreementConstants.AgreementType.UNIT_HEAT_METER:
                    if(equipment.getAddress().equals("83610643000000")){
                        System.out.println(111);
                    }
                    //单元热量表协议
                    meterReadPo.setHeatAddress(equipment.getAddress());
                    meterReadPo.setHeatDtuId(equipment.getDtuId());
                    List<DeviceInstruction> heatList = monitorService.readBuildUnitHeatMeter(meterReadPo);
                    if(CollectionUtils.isNotEmpty(heatList)){
                        deviceInstructionList.addAll(heatList);
                    }
                    break;
                case AgreementConstants.AgreementType.SEAHORSE_VALVE_METER:
                    //海龙马调节阀协议
                    if(equipment.getAddress().equals("83610643000000")){
                        System.out.println(111);
                    }
                    meterReadPo.setValveAddress(equipment.getAddress());
                    meterReadPo.setValveDtuId(equipment.getDtuId());
                    meterReadPo.setHouseholdId(equipment.getHouseholdId());
                    deviceInstruction = monitorService.readRegulatingValveValveMeter(meterReadPo);
                    break;
                default:
                    break;
            }
            if (deviceInstruction != null) {
//                deviceInstruction.setAddress(equipment.getAddress());
                deviceInstructionList.add(deviceInstruction);
                String oldAddress = deviceInstruction.getAddress();
                String newAddress = deviceInstruction.getInstructionSet().replaceAll("FE" , "").substring(4 , 18);
                if(!oldAddress.equals(newAddress)){
                    System.out.println("地址不相同，原地址为："+oldAddress+"。新地址为："+newAddress);
                }
            }

        }
        if (CollectionUtils.isNotEmpty(deviceInstructionList)) {
            deviceInstructionList = deviceInstructionList.stream()
                    .collect(Collectors.toMap(
                            obj -> Arrays.asList(obj.getDtuId(), obj.getAddress(), obj.getInstructType(), obj.getProtocolType()), // 使用多个字段组合作为键
                            obj -> obj,
                            (existing, replacement) -> existing // 如果存在重复，保留第一个
                    ))
                    .values()
                    .stream()
                    .collect(Collectors.toList());

            deviceInstructionList.stream().forEach(item -> {
                item.setBusinessType(businessType);
                item.setExceedTimes(exceedTimes);
            });
            this.saveBatch(deviceInstructionList);
        }

        for(DeviceInstruction d : deviceInstructionList){
            if(d.getAddress().equals("83610643000000")){
                System.out.println(111);
            }
        }
    }

    public void batchInsert(List<DeviceInstruction> deviceInstructionList) {
        this.baseMapper.batchInsert(deviceInstructionList);
    }

    public List<DeviceInstructionStatusVo> getLastStatus() {
        return this.baseMapper.getLastStatus();
    }
}
